/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID coidgen tool.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Robo Strycek
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */



#include "coidgen.h"
#include "coid/comm/binstream/enc_hexstream.h"
#include "coid/comm/binstream/inoutstream.h"
#include "coid/comm/binstream/cachestream.h"
#include "os.h"





const ushort BINARY_DATA_FORMAT = 0x0006;


extern const char STR_OLD_HEADER_INFO_SEARCH[];
extern char TEMPL_CLIENT_IN_CPP[];
extern char TEMPL_INLINE_FNC[];
extern char TEMPL_INLINE_FNC_NOLOCK[];






void write_bin_dispatch_data( charstr& out, H_FILE& h, const char * ver_coidgen, const char * ver_coid,
								size_t source_size, uint source_time )
{
	binstreambuf buf;
	enc_hexstream hex( NULL, &buf );
    hex.setup(96);

	hex << (ushort) BINARY_DATA_FORMAT << ver_coidgen << ver_coid << source_size << source_time;
	hex << h.header.ver << h.ver_next_major;

	hex << h;
	hex.flush();

	out += buf;
}




/** return values:
	0 == no data found, 1 == ok, -1 == crash, 0x10 == coidgen major version changed, 0x20 == binary format changed
*/
int read_bin_dispatch_data( binstream& from, bool binary, H_FILE& h, const char * current_coidgen_ver, charstr& ver_coidgen, charstr& ver_coid,
								size_t * source_size, uint* source_time )
{
    cachestream cch(from);
    binstream* buf = &from;

	if( !binary )
    {
        opcd e = cch.read_until( substring(STR_OLD_HEADER_INFO_SEARCH), 0 );
        if(e)  return 0;

        e = cch.read_until( substring('='), 0 );
        if(e)  return -1;

        buf = &cch;
	}


	try {
		enc_hexstream hex( buf, NULL );

		ushort bin_version;

		//standard header
		size_t size;
        uint time;
		hex >> bin_version >> ver_coidgen >> ver_coid >> size >> time;
		hex >> h.header.ver >> h.ver_next_major;
		if( source_size ) *source_size = size;
		if( source_time ) *source_time = time;

		version stored_coidgen( ver_coidgen );
		version current_coidgen( current_coidgen_ver );
		if( current_coidgen.get_major() != stored_coidgen.get_major() )
			return BINARY_DATA_FORMAT != bin_version ? (0x10 | 0x20) : 0x10;

		if( BINARY_DATA_FORMAT != bin_version )
			return 0x20;


		hex >> h;
	}
	catch(...) {
		return -1;
	}


	return 1;
}







namespace coid {






void BLOCK::stream_to( charstr& out, OUTPUT_TYPE t ) const
{
	uint s1 = sizeof(STR_client_only_L) - 1;
	const char * to;
	if( t == T_client_header && (flags & fclient) ) {
		const char * ptr = text.ptr();
		while( 1 ) {
			to = strchr( ptr, '}' );
			if( !to ) break;
			const char * to2 = to + 1;
			while( *to2 == ' ' || *to2 == '\t' || *to2 == '\r' || *to2 == '\n' )
				to2++;
			const char * what = (flags & fclient_only) ? STR_client_only_R : "client";
			uint size = (flags & fclient_only) ? sizeof(STR_client_only_R)-1 : sizeof("client")-1;
			if( strncmp(to2, what, size) == 0 )
				break;
			ptr = to + 1;
		}

		if( !to ) {
			if( flags & fadd_to_client ) {			/// "//}client" was not found
				printf( "Ignoring '//{client' block: cannot find end of block (//}client)\n" );
				return;
			}
			else {
				to = strstr( (char*)text.ptr(), "*/" );			/// try to find end of comment
				if( !to ) {
					printf( "Ignoring /*{client_only block: cannot find end of block (*/)\n" );
					return;
				}
			}
		}

		if( flags & fadd_to_client ) {
			to -= 2;
			s1 = sizeof(STR_client_L) - 1;
		}
	}
	else {
		return;
	}


	if( *(text.ptr() + s1) == '\r' ) s1++;
	if( *(text.ptr() + s1) == '\n' ) s1++;

	out.add_from( text.ptr() + s1, to - text.ptr() - s1 );
	out << EOL;
}









charstr& operator << ( charstr& out, const HEADER & x )
{
	out << "/**\t\\author    " << x.author;
	out << EOL "\t\\version   ";
	//x.ver.get_internal_version( out );
	x.ver.get_version( out, true );
	out << EOL "\t\\date      " << x.date;
	out << EOL "\t\\brief     " << x.description;
	out << EOL "*/" EOL EOL;

	return out;
}










void H_FILE::stream_to( charstr& out, OUTPUT_TYPE t ) const
{
	/// file header
	out << header;

	/// #ifndef
	charstr str_ifdef;
	if( t == T_client_header ) {
		str_ifdef = "__COID_CLIENT__";
		str_ifdef += source_header_name;
		replace_char( (char*)str_ifdef.ptr(), '.', '_' );
		str_ifdef += EOL;
		out << "#ifndef " << str_ifdef;
		out << "#define " << str_ifdef << EOL EOL;
	}

	/// header #define
	const char * p = str_ifdef.ptr() + sizeof( "__COID_" ) - 1;
	out << "#define " << p << EOL EOL;


	if( (t == T_client_class_in_cpp || t == T_client_header) && header_info ) {
		RASSERT( cls.size() );
		replace_template( false, header_info, out, cls[0] );
	}


	uint i = 0, j = 0;
	for( i=0; i<cls.size(); i++ ) {
		for( ; j < blocks.size() && blocks[j].index_before_class == i; j++ ) {
			blocks[j].stream_to( out, t );
		}
		out << EOL EOL;

		/// function table
		if( t == T_client_header ) {
			//out << EOL "namespace %namespace% {" EOL EOL;

			CLASS * c = cls[i];
            replace_template( false, EOL "namespace %namespace% {" EOL EOL, out, c );
			replace_template( false, EOL "struct " STR_fnc_table_name ";" EOL EOL, out, c );
		}

		cls[i]->stream_to( out, t );
		if( t == T_client_header )
            replace_template( false, "} // namespace %namespace%" EOL EOL, out, cls[i] );
            //out << "} // namespace %namespace%" EOL EOL;
	}


	if( t == T_client_header )
		out << EOL "#endif\t// ! " << str_ifdef;
}




binstream & operator << ( binstream & out, const H_FILE & x )
{
	out << x.cls.size();
	for( uint i=0; i<x.cls.size(); i++ )
		out << *x.cls[i];
	return out << x.flags;
}



binstream & operator >> ( binstream & in, H_FILE & x )
{
	x.source_header_name = x.client_header_name = NULL;
	uint size;
	in >> size;
	if( size ) {
		x.cls.add( size );
		for( uint i=0; i<size; i++ ) {
			x.cls[i] = new CLASS;
			in >> *x.cls[i];
		}
	}
	return in >> x.flags;
}









void CLASS::stream_to( charstr& out, OUTPUT_TYPE t ) const
{
	uint i;

	out << EOL;
	if( t != T_client_class_in_cpp && main_comment.len() ) out << main_comment;

	out << name_left << " " << name;
	if( t == T_client_class_in_cpp ) out << "_localclient";
	else if( t == T_client_header ) out << "_client";

	//if( t == T_client_class_in_cpp ) out << " : public COID_CLIENT";
    if( !name_right.is_empty() ) out << name_right;
	out << EOL "{" EOL;

    if( t == T_client_class_in_cpp ) {
        out << "public:" EOL "\tCOID_CLIENT cc;" EOL EOL;
        file->replace_template( false, "\tstatic void coid_init_table( uint coid_stream_flags, COID_TABLE_%name%_%_version_% *& table );" EOL,
            out, this );
    }

	if( options.len() && t == T_client_header ) {
		out << "\t//" << STR_coid_options << options;
		out << EOL;
	}
	if( dependencies.len() ) {
		out << "\t//" << STR_coid_dependencies << " " << dependencies;
		out << EOL;
	}


	if( t == T_client_header ) {
		uint cls_index = file->find_class( this );
		for( uint j=0; j < blocks.size() && blocks[j].index_before_class == cls_index; j++ ) {
			if( blocks[j].offset > class_text_offset && blocks[j].offset < class_text_offset_end )
				blocks[j].stream_to( out, t );
		}
	}


	if( (t == T_client_class_in_cpp || t == T_client_header) && file->class_members )
		file->replace_template( false, file->class_members, out, this );


	out << "public:" EOL;

	if( t == T_client_class_in_cpp ) {
		file->replace_template( false, TEMPL_CLIENT_IN_CPP, out, this );
	}
	else {
		for( i=0; i<=CLASS::accept_connect_shared; i++ ) {	/// only 2 connect*() functions can be present in the client
				/// do not generate connect() method for client for class with accept_auto_connect() method and missing accept_connect() method
			if( i == CLASS::accept_connect && (flags & CLASS::fautorun) &&
					(fnc_special[CLASS::accept_connect]->flags & FUNCTION::fnot_present_in_service) )
			{
				continue;
			}
			fnc_special[i]->stream_to( out, t );
			out << EOL;
			if( i == CLASS::accept_connect ) {
				fnc_special[i]->flags |= FUNCTION::faccept_connect_within;
				fnc_special[i]->stream_to( out, t );
				out << EOL;
				fnc_special[i]->flags &= ~FUNCTION::faccept_connect_within;
			}
		}
		out << EOL;
	}

	for( i=0; i<fnc_export.size(); i++ ) {
		if( t == T_client_class_in_cpp && (fnc_export[i]->flags & FUNCTION::fcomm_mask) == FUNCTION::fdirect )
			out << "// ";

		fnc_export[i]->stream_to( out, t );
		out << EOL;
	}

	out << "};" EOL EOL;
}




binstream & operator << ( binstream & out, const CLASS & x )
{
	out << x.name << x.short_name << x.options << x.dependencies;
	out << x.max_fnc_index << x.fnc_index;
	out << x.fnc_export.size();
	uint i;
	for( i=0; i<x.fnc_export.size(); i++ )
		out << *x.fnc_export[i];

	for( i=0; i<CLASS::SPEC_FNC_COUNT; i++ ) {
		if( x.fnc_special[i] == NULL ) {
			out << (bool) false;
			continue;
		}
		out << (bool) true;
		out << *x.fnc_special[i];
	}

	return out << x.flags << x.acceptor_port;
}




binstream & operator >> ( binstream & in, CLASS & x )
{
	in >> x.name >> x.short_name >> x.options >> x.dependencies;
	in >> x.max_fnc_index >> x.fnc_index;

	uint size;
	in >> size;
	if( size ) {
		x.fnc_export.add( size );
		for( uint i=0; i<size; i++ ) {
			x.fnc_export[i] = new FUNCTION;
			in >> *x.fnc_export[i];
		}
	}
	bool tmp;
	for( uint i=0; i<CLASS::SPEC_FNC_COUNT; i++ ) {
		x.fnc_special[i] = NULL;
		in >> tmp;
		if( tmp ) {
			x.fnc_special[i] = new FUNCTION;
			in >> *x.fnc_special[i];
		}
	}
	x.parent = NULL;
	x.file = NULL;

	return in >> x.flags >> x.acceptor_port;
}







void FUNCTION::stream_to( charstr& out, OUTPUT_TYPE t ) const
{
	RASSERT( parent );

	bool client_hook = t == T_client_function_hook;
	if( client_hook ) t = T_function_hook;

	if( t == T_client_header ) {
		if( main_comment.len() ) out << "\t" << main_comment;
		out << "\t";
	}

	bool b_accept_connect = (flags & fspecial_fnc) && this == parent->fnc_special[accept_connect];
	bool b_accept_connect_shared = (flags & fspecial_fnc) && this == parent->fnc_special[accept_connect_shared];

	if( t == T_client_class_in_cpp ) out << "\t";

	ret.stream_to( out, t );

	/// name:
	if( t == T_function_table || t == T_function_hook ) {
		out << "(";
		if( t == T_function_table )
			parent->file->replace_template( false, STR_fnc_table_name, out, parent );
		else out << parent->name;
		if( client_hook ) out << "_localclient";
		out << "::*";
	}
	else if( t == T_client_cpp || t == T_client_cpp_inline_fnc ) {
		out << parent->name;
		if( b_accept_connect || b_accept_connect_shared || t == T_client_cpp_inline_fnc ) out << "_client::";
		else out << "_localclient::";
	}

	if( t != T_function_hook ) {
		uint name_offset = 0;
		if( (flags & fspecial_fnc) && t != T_dispatch_cpp )
			name_offset += sizeof(STR_accept) - 1;
		out << name.ptr() + name_offset;
		if( t == T_function_table && this != &parent->fnc_disconnect )
			out << "__" << id;
		if( t == T_client_cpp || t == T_client_header ) {
			if( flags & faccept_connect_within ) out << "_within";
		}
	}
	if( t == T_function_table || t == T_function_hook ) out << ")";

	if( (flags & fspace_after_name) && t != T_function_hook ) out << " ";
	out << "(";
	if( args.size() && (flags & fspace_in_brackets) ) out << " ";



	if( (t == T_client_class_in_cpp || t == T_client_cpp || t == T_client_header) ) {
		if( b_accept_connect_shared ) out << " uint coid_obj_id, ";
		else if( b_accept_connect ) out << " ";
	}

	/// arguments:
	uint count = 0;
	if( flags & faccept_connect_within ) {		/// no additional checks are required
		out << "comm_mutex_reg & coid_channel";
		count++;
	}

	for( uint i=0; i<args.size(); i++ ) {
		if( args[i].flags & ARG::faccount )
			continue;
		if( count ) out << ", ";
		count++;
		args[i].stream_to( out, client_hook ? T_client_function_hook : t );
	}

	/// last arguments in connect*() functions
	if( !(flags & faccept_connect_within) ) {
		if( (t == T_client_class_in_cpp || t == T_client_cpp || t == T_client_header) && (b_accept_connect || b_accept_connect_shared) ) {
			if( count ) out << ", ";
			else if( flags & fspace_in_brackets ) out << " ";
			out << "const char * coid_address";
			if( t == T_client_class_in_cpp || t == T_client_header ) out << "=NULL";
			out << ", uint coid_flags";
			if( t == T_client_class_in_cpp || t == T_client_header ) out << "=ConnectFlags::xACCESS_MODE";
			if( count == 0 && (flags & fspace_in_brackets) ) out << " ";
		}
	}

	if( count && (flags & fspace_in_brackets) ) out << " ";
	else if( (t == T_client_class_in_cpp || t == T_client_cpp || t == T_client_header) ) {
		if( b_accept_connect_shared || b_accept_connect )
			out << " ";
	}

	out << ")";
	if( flags & fconst ) out << " const";

/////////////////////////////////////////////////////////////
	/// implementation of inline functions
/////////////////////////////////////////////////////////////
	if( t == T_client_cpp_inline_fnc && ! (flags & fspecial_fnc) )
		stream_body_to( out, t );
	else if( t == T_client_header || t == T_client_class_in_cpp || t == T_function_table || t == T_client_header ) {
		out << ";";
	}
}


void FUNCTION::stream_body_to( charstr& out, OUTPUT_TYPE t ) const
{
	bool is_opcd = opcd_return_type();
	bool is_void = void_return_type();
	const char * var_name = "_coid_err_code";
	if( !is_opcd ) var_name = STR_coid_return_value;

	out << EOL "{" EOL;

	if( !is_void ) {
		out << "\t" << ret.type << " ";
		if( ret.asterix_count == 1 ) out << "* ";
		out << var_name << ";" EOL;
	}

	uint index = parent->get_function_index( this );
    if( flags & fnolock )
	    parent->file->replace_template( false, TEMPL_INLINE_FNC_NOLOCK, out, parent, index );
    else
        parent->file->replace_template( false, TEMPL_INLINE_FNC, out, parent, index );


	if( is_opcd ) out << "_coid_err_code = e;}" EOL;
	else {
		if( !is_void ) out << "_coid_return_value = 0; ";
		out << "set_last_error( e );}" EOL;
	}
	out << "#endif" EOL;

	/// return
	if( !is_void )
		out << "\treturn " << var_name << ";" EOL;
	out << "}";
}


FUNCTION & FUNCTION::operator = ( const FUNCTION & x )
{
	parent = x.parent;
	ret = x.ret;
	name = x.name;
	id = x.id;
	main_comment = x.main_comment;		/// comment above function
	open_service = x.open_service;
	args = x.args;
	for( uint i=0; i<args.size(); i++ ) {
		args[i].parent = this;
		//((FUNCTION &) x).args[i].arg_count_eval = NULL;		/// this arg would be deleted later
		if( args[i].type_remap ) args[i].type_remap->parent = this;
	}
	flags = x.flags;
	handle_client_calls = x.handle_client_calls;

	return *this;
}




binstream & operator << ( binstream & out, const FUNCTION & x )
{
	return out << x.ret << x.name << x.id << x.args << x.flags << x.handle_client_calls << x.open_service;
}


binstream & operator >> ( binstream & in, FUNCTION & x )
{
	x.parent = NULL;
	return in >> x.ret >> x.name >> x.id >> x.args >> x.flags >> x.handle_client_calls >> x.open_service;
}










void ARG::stream_to( charstr& out, OUTPUT_TYPE t ) const
{
	bool is_client = t != T_function_hook && t != T_dispatch_cpp;
	if( type_remap && is_client ) {
		type_remap->stream_to( out, t );
		return;
	}

	bool bopen_service = (t == T_client_header || t == T_client_cpp_inline_fnc) && (flags & fopen_service) != 0;

	if( t == T_client_header ) {
		if( flags & fptr_count ) out << STR_net_countname << count_string << " ";
	}
	if( bopen_service ) out << parent->open_service << "_client";

	uint count = asterix_count;
	if( flags & fptr ) {
		if( flags & ffetch ) {
			if( (flags & fptr_count) == 0 && t != T_function_table )
				count--;
		}
	}

	if( type.len() ) {
		if( !bopen_service ) {
			if( t != T_function_table && (flags & (fconst | ffetch)) == (fconst | ffetch) ) {
				charstr tmp;
				H_FILE::remove_const( type, tmp );
				out << tmp;
			}
			else out << type;

			if( count ) out << " ";
			for( uint i=0; i<count; i++ )
				out << "*";
		}

		if( flags & fref ) {
			if( !(flags & freturn) ) out << " &";
			else if( t == T_dispatch_cpp || t == T_function_hook || t == T_client_function_hook )
				out << " &";
		}
		out << " ";
	}

	if( bopen_service ) {
		charstr tmp = "__coid_";
		tmp << parent->open_service << "_client";
		out << tmp;
	}
	else if( name.len() && t != T_function_hook && t != T_client_function_hook )
		out << name;

	if( def_value.len() ) {
		if( t == T_client_header )
			out << "=" << def_value;
	}
}



ARG & ARG::operator = ( const ARG & x )
{
	//RASSERT( type_remap == NULL );

	parent = NULL;
	arg_count = x.arg_count;
	type_remap = NULL;
	if( x.type_remap ) {
		type_remap = new ARG;
		*type_remap = *x.type_remap;
	}

	type = x.type; name = x.name;
	count_string = x.count_string;
	def_value = x.def_value;
	asterix_count = x.asterix_count;
	flags = x.flags;

	if( arg_count_eval ) {delete arg_count_eval; arg_count_eval = NULL;}
	if( x.flags & ARG::fptr_count )
		arg_count_eval = x.create_arg_count_eval();

	return *this;
}




binstream & operator << ( binstream & out, const ARG & x )
{
	out << x.arg_count << x.type << x.count_string << x.def_value << x.asterix_count << x.flags;
	out << (bool) (x.type_remap != NULL);
	if( x.type_remap ) out << *x.type_remap;
	return out;
}



binstream & operator >> ( binstream & in, ARG & x )
{
	x.parent = NULL; x.arg_count_eval = NULL; x.type_remap = NULL;
	in >> x.arg_count >> x.type >> x.count_string >> x.def_value >> x.asterix_count >> x.flags;
	bool type_remap;
	in >> type_remap;
	if( type_remap ) {
		x.type_remap = new ARG;
		in >> *x.type_remap;
	}

	if( x.flags & ARG::fptr_count )
		x.arg_count_eval = x.create_arg_count_eval();

	return in;
}






/// streaming of client args
void FUNCTION::client_streaming_to_stream( charstr& out, bool cci )
{
	charstr tmp;
	ARG * a = args.ptr();
	ARG * tmpa = NULL;

    token cc = cci ? "cc." : "";

	for( uint i=0; i<args.size(); i++, a++ ) {
		if( !(a->flags & (ARG::fin | ARG::finout)) ) continue;

		if( a->type_remap ) {
			tmpa = a; a = a->type_remap;
		}


		bool b_write_array = a->is_localarray();
		tmp.reset();
		 if( a->flags & ARG::finout ) {
			if( a->asterix_count == 2 || (a->asterix_count == 1 && ! (a->flags & ARG::fref)) ) tmp << "*";
		}
		a->append_client_arg_name( tmp, ARG::adt_name );

		out << "\t";
		/// stream it into bstream
		if( b_write_array ) {
			if( a->count_string.len() && a->arg_count != UMAX32 )		/// stream it only if it is not constant
				out << "*" << cc << "_coid_bstream << (uint)" << a->count_string << ";" EOL "\t";
			out << cc << "_coid_bstream->write_linear_array( " << tmp << ", (uint)";
			out << a->count_string << " );" EOL;
		}
		else {
			out << "*" << cc << "_coid_bstream << ";
			if( a->flags & ARG::fnet_ptr ) out << STR_net_ptr_cast_src;
			out << tmp << ";" EOL;
		}

		if( (a->flags & (ARG::finout | ARG::fptr_count)) == (ARG::finout | ARG::fptr_count) ) {
			if( a->flags & ARG::fmalloc ) out << "\tfree( " << tmp << " );" EOL;
			else out << "\tdelete " << tmp << ";" EOL;
		}

		if( tmpa ) {a = tmpa; tmpa = NULL;}
	}
}



void FUNCTION::client_streaming_from_stream( charstr& out, bool cci )
{
	charstr tmp;

	if( !void_return_type() ) {
		if( opcd_return_type() ) ;//out << "\t_coid_return_value = _coid_err_code;" EOL;
		else out << "\t*cc._coid_bstream >> cc._coid_return_value;" EOL;
	}

    token cc = cci ? "cc." : "";

	ARG * a = args.ptr();
	ARG * tmpa = NULL;
	ARG * arg_count_tmp = NULL;
	for( uint i=0; i<args.size(); i++, a++ ) {
		if( !(a->flags & (ARG::finout | ARG::fout | ARG::ffetch)) ) continue;

		if( a->type_remap ) {
			tmpa = a; a = a->type_remap;
		}


		if( a->count_string.len() && a->arg_count != UMAX32 && arg_count_tmp == NULL ) {
			/// count must be streamed before the contents
			arg_count_tmp = a;
			a = a->arg_count_eval;
			RASSERT( a );
		}
		else arg_count_tmp = NULL;

		bool b_read_array = (a->flags & ARG::fptr_count) != 0;
		tmp.reset();
		if( arg_count_tmp == NULL && ! (a->flags & ARG::fref) ) tmp << "*";
		a->append_client_arg_name( tmp, ARG::adt_name );

		out << "\t";
		if( arg_count_tmp ) {
			out << a->type << " " << tmp << ";" EOL "\t";
		}


		if( b_read_array ) {
			charstr tmp_type;
			H_FILE::remove_const( a->type, tmp_type );

			// alloc
			if( a->flags & ARG::fmalloc )
                out << "binstream_container_with_malloc<" << tmp_type << "> cont((uint)";
			else
                out << "binstream_container_with_new<" << tmp_type << "> cont((uint)";

            a->append_client_arg_name( out, ARG::adt_count_name );
            out << ");" << EOL "\t";
            out << cc << "_coid_bstream->read_array(cont);" EOL "\t";
            out << tmp << " = cont.ptr();" EOL;
        }
		else {
			out << "*" << cc << "_coid_bstream >> ";
			if( a->flags & ARG::fnet_ptr ) out << STR_net_ptr_cast_dest;
            if( a->type.begins_with("enum") && (a->type.nth_char(4)==' ' || a->type.nth_char(4)=='\t') ) {
                out << "(EnumType<sizeof(";
                a->append_client_arg_name( out, ARG::adt_name );
                out << ")>::TEnum&)";
            }
			out << tmp << ";" EOL;
		}

		if( arg_count_tmp ) {
			a = arg_count_tmp - 1; i--;
		}

		if( tmpa ) {a = tmpa; tmpa = NULL;}
	}
}



} // namespace coid
